package com.mlx.mbg.code.plugin;

import com.mlx.mbg.code.MbgUtil;
import com.mlx.mbg.code.config.*;
import com.mlx.mbg.code.inter.EnableMethod;
import com.mlx.mbg.code.inter.ExtensionLevel;
import com.mlx.mbg.code.inter.IExtensionMethod;
import org.mybatis.generator.api.GeneratedJavaFile;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.PluginAdapter;
import org.mybatis.generator.api.dom.java.*;
import org.mybatis.generator.api.dom.xml.Attribute;
import org.mybatis.generator.api.dom.xml.Document;
import org.mybatis.generator.api.dom.xml.TextElement;
import org.mybatis.generator.api.dom.xml.XmlElement;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

public class MbgPlugin extends PluginAdapter{

    protected MbgPropertyConfig mbgPropertyConfig = new MbgPropertyConfig();

    protected ServicePropertyConfig servicePropertyConfig;

    protected ControllerPropertyConfig controllerPropertyConfig;

    protected SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    protected List<IExtensionMethod> extensionMethodList;


    @Override
    public boolean validate(List<String> list) {
        readMbgPropertyConfig();
        readServicePropertyConfig();
        readControllerPropertyConfig();
        readExtensionMethod();
        return true;
    }

    private void readMbgPropertyConfig() {
        String enableLombok = properties.getProperty("enableLombok", "true");
        String enableSwagger = properties.getProperty("enableSwagger", "true");

        String enableSelectByIdCollection = properties.getProperty("enableSelectByIdCollection", "true");
        String enableDeleteByIdCollection = properties.getProperty("enableDeleteByIdCollection", "true");
        String enableListSelective = properties.getProperty("enableListSelective", "true");
        String enableInsertBatch = properties.getProperty("enableInsertBatch", "true");
        String enableUpdateBatch = properties.getProperty("enableUpdateBatch", "true");
        String enableSelectOne = properties.getProperty("enableSelectOne", "true");
        String enableSelectMultiple = properties.getProperty("enableSelectMultiple", "true");
        String enableSelectCollection = properties.getProperty("enableSelectCollection", "true");
        String enableListQuery = properties.getProperty("enableListQuery", "true");

        String enableService = properties.getProperty("enableService", "false");
        String enableController = properties.getProperty("enableController", "false");
        String enableSerializable = properties.getProperty("enableSerializable", "true");

        String enableDeleteCondition = properties.getProperty("enableDeleteCondition", "true");
        String enableDeleteCollection = properties.getProperty("enableDeleteCollection", "true");

        // 设置配置
        mbgPropertyConfig.setEnableLombok("true".equals(enableLombok));
        mbgPropertyConfig.setEnableSwagger("true".equals(enableSwagger));

        mbgPropertyConfig.setEnableSelectByIdCollection("true".equals(enableSelectByIdCollection));
        mbgPropertyConfig.setEnableDeleteByIdCollection("true".equals(enableDeleteByIdCollection));
        mbgPropertyConfig.setEnableListSelective("true".equals(enableListSelective));
        mbgPropertyConfig.setEnableInsertBatch("true".equals(enableInsertBatch));
        mbgPropertyConfig.setEnableUpdateBatch("true".equals(enableUpdateBatch));
        mbgPropertyConfig.setEnableSelectOne("true".equals(enableSelectOne));
        mbgPropertyConfig.setEnableSelectMultiple("true".equals(enableSelectMultiple));
        mbgPropertyConfig.setEnableSelectCollection("true".equals(enableSelectCollection));

        mbgPropertyConfig.setEnableListQuery("true".equals(enableListQuery));

        mbgPropertyConfig.setEnableService("true".equals(enableService));
        mbgPropertyConfig.setEnableController("true".equals(enableController));
        mbgPropertyConfig.setEnableSerializable("true".equals(enableSerializable));

        mbgPropertyConfig.setEnableDeleteCondition("true".equals(enableDeleteCondition));
        mbgPropertyConfig.setEnableDeleteCollection("true".equals(enableDeleteCollection));

    }

    private void readServicePropertyConfig() {
        if (mbgPropertyConfig.getEnableService()){
            String serviceTargetProject = properties.getProperty("serviceTargetProject");
            String serviceImplTargetProject = properties.getProperty("serviceImplTargetProject");

            String servicePackage = properties.getProperty("servicePackage");
            String serviceImplPackage = properties.getProperty("serviceImplPackage");

            if (MbgUtil.isEmpty(serviceTargetProject)){
                throw new RuntimeException("serviceTargetProject为空");
            }
            if (MbgUtil.isEmpty(serviceImplTargetProject)){
                throw new RuntimeException("serviceImplTargetProject为空");
            }

            if (MbgUtil.isEmpty(servicePackage)){
                throw new RuntimeException("servicePackage为空");
            }
            if (MbgUtil.isEmpty(serviceImplPackage)){
                throw new RuntimeException("serviceImplPackage为空");
            }


            String servicePrefix = properties.getProperty("servicePrefix");
            servicePrefix = MbgUtil.isEmpty(servicePrefix) ? "" : servicePrefix;
            String serviceSuffix = properties.getProperty("serviceSuffix");
            serviceSuffix = MbgUtil.isEmpty(serviceSuffix) ? "Service" : serviceSuffix;

            String serviceImplPrefix = properties.getProperty("serviceImplPrefix");
            serviceImplPrefix = MbgUtil.isEmpty(serviceImplPrefix) ? "" : serviceImplPrefix;
            String serviceImplSuffix = properties.getProperty("serviceImplSuffix");
            serviceImplSuffix = MbgUtil.isEmpty(serviceImplSuffix) ? "ServiceImpl" : serviceImplSuffix;
            String mapperFieldName = properties.getProperty("mapperFieldName");
            mapperFieldName = MbgUtil.isEmpty(mapperFieldName) ? "mapper" : mapperFieldName;

            servicePropertyConfig = new ServicePropertyConfig();

            servicePropertyConfig.setServiceTargetProject(serviceTargetProject);
            servicePropertyConfig.setServiceImplTargetProject(serviceImplTargetProject);

            servicePropertyConfig.setServicePackage(servicePackage);
            servicePropertyConfig.setServiceImplPackage(serviceImplPackage);

            servicePropertyConfig.setServicePrefix(servicePrefix);
            servicePropertyConfig.setServiceSuffix(serviceSuffix);

            servicePropertyConfig.setServiceImplPrefix(serviceImplPrefix);
            servicePropertyConfig.setServiceImplSuffix(serviceImplSuffix);

            servicePropertyConfig.setMapperFieldName(mapperFieldName);

        }
    }

    private void readControllerPropertyConfig() {

        if (mbgPropertyConfig.getEnableController()){
            String controllerTargetProject = properties.getProperty("controllerTargetProject");
            String controllerPackage = properties.getProperty("controllerPackage");

            String responseBeanClassName = properties.getProperty("responseBeanClassName");
            String responseBeanStaticMethodName = properties.getProperty("responseBeanStaticMethodName");

            String servicePackage = properties.getProperty("servicePackage");

            if (MbgUtil.isEmpty(controllerTargetProject)){
                throw new RuntimeException("controllerTargetProject为空");
            }
            if (MbgUtil.isEmpty(controllerPackage)){
                throw new RuntimeException("controllerPackage为空");
            }
            if (MbgUtil.isEmpty(servicePackage)){
                throw new RuntimeException("servicePackage为空");
            }

            String controllerPrefix = properties.getProperty("controllerPrefix");
            controllerPrefix = MbgUtil.isEmpty(controllerPrefix) ? "" : controllerPrefix;
            String controllerSuffix = properties.getProperty("controllerSuffix");
            controllerSuffix = MbgUtil.isEmpty(controllerSuffix) ? "Controller" : controllerSuffix;
            String enableSQLExtension = properties.getProperty("enableSQLExtension");
            enableSQLExtension = MbgUtil.isEmpty(enableSQLExtension) ? "false" : enableSQLExtension;
            String serviceFieldName = properties.getProperty("serviceFieldName");
            serviceFieldName = MbgUtil.isEmpty(serviceFieldName) ? "service" : serviceFieldName;


            controllerPropertyConfig = new ControllerPropertyConfig();

            controllerPropertyConfig.setControllerTargetProject(controllerTargetProject);
            controllerPropertyConfig.setControllerPackage(controllerPackage);
            controllerPropertyConfig.setControllerPrefix(controllerPrefix);
            controllerPropertyConfig.setControllerSuffix(controllerSuffix);
            controllerPropertyConfig.setResponseBeanClassName(responseBeanClassName);
            controllerPropertyConfig.setResponseBeanStaticMethodName(responseBeanStaticMethodName);
            controllerPropertyConfig.setEnableSQLExtension("true".equals(enableSQLExtension));
            controllerPropertyConfig.setServiceFieldName(serviceFieldName);
        }

    }

    private void readExtensionMethod(){
        List<Class<?>> classList = MbgUtil.packageScanClass(
                "com.mlx.mbg.code.method",
                false,
                clazz -> IExtensionMethod.class.isAssignableFrom(clazz) && MbgUtil.isHaveAnnotation(clazz, EnableMethod.class));

        extensionMethodList = new ArrayList<>();

        for (Class<?> clazz : classList){
            try {
                IExtensionMethod instance =(IExtensionMethod) clazz.newInstance();
                extensionMethodList.add(instance);
            } catch (InstantiationException e) {
                throw new RuntimeException(String.format("扩展方法接口【%s】无参构造方法执行异常", clazz.getName()));
            } catch (IllegalAccessException e) {
                throw new RuntimeException(String.format("没有扩展方法接口【%s】无参构造方法访问权限", clazz.getName()));
            }
        }

        extensionMethodList.sort(Comparator.comparing(IExtensionMethod::getOrder));

        System.out.println("mbg-code获取到了【" + extensionMethodList.size() + "】个扩展方法");
    }


    @Override
    public List<GeneratedJavaFile> contextGenerateAdditionalJavaFiles(IntrospectedTable introspectedTable) {

        List<GeneratedJavaFile> answer = new ArrayList<>();

        String tableName = introspectedTable.getFullyQualifiedTableNameAtRuntime();

        if (mbgPropertyConfig.getEnableService()){

            String serviceTargetProject = servicePropertyConfig.getServiceTargetProject();
            String servicePackage = servicePropertyConfig.getServicePackage();
            if (MbgUtil.isEmpty(serviceTargetProject)){
                System.out.printf("【未配置【serviceTargetProject】，无法生成【%s】表service接口%n", tableName);
                return null;
            }
            if (MbgUtil.isEmpty(servicePackage)){
                System.out.printf("【未配置【servicePackage】，无法生成【%s】表service接口%n", tableName);
                return null;
            }

            String serviceImplTargetProject = servicePropertyConfig.getServiceImplTargetProject();
            String serviceImplPackage = servicePropertyConfig.getServiceImplPackage();
            if (MbgUtil.isEmpty(serviceImplTargetProject)){
                System.out.printf("【未配置【serviceImplTargetProject】，无法生成【%s】表service实现类%n", tableName);
                return null;
            }
            if (MbgUtil.isEmpty(serviceImplPackage)){
                System.out.printf("【未配置【serviceImplPackage】，无法生成【%s】表service实现类%n", tableName);
                return null;
            }

            GeneratedJavaFile generatedJavaFileService = generateServiceInterface(introspectedTable);
            GeneratedJavaFile generatedJavaFileServiceImpl = generateServiceImpl(introspectedTable);
            if (MbgUtil.isNotEmpty(generatedJavaFileService)){
                answer.add(generatedJavaFileService);
            }
            if (MbgUtil.isNotEmpty(generatedJavaFileServiceImpl)){
                answer.add(generatedJavaFileServiceImpl);
            }
        }

        if (mbgPropertyConfig.getEnableController()){

            String controllerTargetProject = controllerPropertyConfig.getControllerTargetProject();
            String controllerPackage = controllerPropertyConfig.getControllerPackage();

            String serviceTargetProject = servicePropertyConfig.getServiceTargetProject();
            String servicePackage = servicePropertyConfig.getServicePackage();

            String serviceImplTargetProject = servicePropertyConfig.getServiceImplTargetProject();
            String serviceImplPackage = servicePropertyConfig.getServiceImplPackage();

            if (MbgUtil.isEmpty(controllerTargetProject)){
                System.out.printf("【未配置【controllerTargetProject】，无法生成【%s】表controller类%n", tableName);
                return null;
            }
            if (MbgUtil.isEmpty(controllerPackage)){
                System.out.printf("【未配置【controllerPackage】，无法生成【%s】表controller实现类%n", tableName);
                return null;
            }
            if (MbgUtil.isEmpty(serviceTargetProject)){
                System.out.printf("【未配置【serviceTargetProject】，无法生成【%s】表controller实现类%n", tableName);
                return null;
            }
            if (MbgUtil.isEmpty(servicePackage)){
                System.out.printf("【未配置【servicePackage】，无法生成【%s】表controller实现类%n", tableName);
                return null;
            }

            if (MbgUtil.isEmpty(serviceImplTargetProject)){
                System.out.printf("【未配置【serviceImplTargetProject】，无法生成【%s】表service实现类%n", tableName);
                return null;
            }
            if (MbgUtil.isEmpty(serviceImplPackage)){
                System.out.printf("【未配置【serviceImplPackage】，无法生成【%s】表service实现类%n", tableName);
                return null;
            }

            GeneratedJavaFile generatedJavaFileController = generateController(introspectedTable);
            if (MbgUtil.isNotEmpty(generatedJavaFileController)){
                answer.add(generatedJavaFileController);
            }
        }
        return answer;
    }



    /**
     * 实体类添加lombok注解和swagger注解
     */
    public boolean modelBaseRecordClassGenerated(TopLevelClass interfaze, IntrospectedTable introspectedTable) {
        if (mbgPropertyConfig.getEnableLombok()){
            interfaze.addImportedType("lombok.Data");
            interfaze.addAnnotation("@Data");

            List<Method> methods = interfaze.getMethods();

            methods.removeIf(obj -> obj.getName().startsWith("get") || obj.getName().startsWith("set") || obj.getName().startsWith("is"));
        }

        if (mbgPropertyConfig.getEnableSwagger()){
            interfaze.addImportedType("io.swagger.annotations.ApiModel");
            interfaze.addImportedType("io.swagger.annotations.ApiModelProperty");
            interfaze.addAnnotation("@ApiModel");

            List<IntrospectedColumn> columnList = introspectedTable.getAllColumns();
            Map<String, IntrospectedColumn> columnMap = columnList.stream().collect(Collectors.toMap(IntrospectedColumn::getJavaProperty, Function.identity()));
            List<Field> fieldList = interfaze.getFields();
            for (Field field : fieldList){

                String javaProperty = field.getName();

                String remark = columnMap.get(javaProperty).getRemarks();
                remark = remark == null ? "" : remark;
                remark = remark.replaceAll("\n", "");
                remark = remark.replaceAll("\r", "");

                String javaType = field.getType().getShortName();
                String example = "";
                if ("String".equals(javaType)){
                    example = MbgUtil.isEmpty(remark) ? javaProperty : remark;
                }else if ("Date".equals(javaType)){
                    example = sdf.format(System.currentTimeMillis());
                }
                String stringBuilder = "@ApiModelProperty(" + "name=" + "\"" + javaProperty + "\"" + ", " +
                        "notes=" + "\"" + remark + "\"" + ", " +
                        "example=" + "\"" + example + "\"" + ", " +
                        "position=100" +
                        ")";
                field.addAnnotation(stringBuilder);
            }
        }

        if (mbgPropertyConfig.getEnableSerializable()){
            FullyQualifiedJavaType javaType = new FullyQualifiedJavaType("java.io.Serializable");
            interfaze.addImportedType(javaType);
            interfaze.addSuperInterface(javaType);
            Field field = new Field("serialVersionUID", new FullyQualifiedJavaType("long"));
            field.setVisibility(JavaVisibility.PRIVATE);
            field.setStatic(true);
            field.setFinal(true);
            field.setInitializationString("1L");
            List<Field> fields = interfaze.getFields();
            fields.add(0, field);
        }

        return true;
    }


    /**
     * MBG扩展xml元素
     */
    @Override
    public boolean sqlMapDocumentGenerated(Document document, IntrospectedTable introspectedTable) {

        // 获取xml文件根节点
        XmlElement rootElement = document.getRootElement();
        // 清理所有元素
        rootElement.getElements().clear();

        addBaseResultMapColumnListXml(rootElement, introspectedTable);

        addSqlLabelXml(rootElement, introspectedTable);

        addRelevanceSelectColumnListXml(rootElement, introspectedTable);


        // 执行扩展方法，扩展方法由子类实现
        List<XmlElement> elementList = new ArrayList<>();
        for (IExtensionMethod extensionMethod : extensionMethodList){
            if (extensionMethod.checkConfig(ExtensionLevel.MAPPER, mbgPropertyConfig, introspectedTable)){
                elementList.addAll(extensionMethod.buildSQL(introspectedTable));
            }
        }

        for (XmlElement element : elementList){
            rootElement.addElement(element);
        }

        // 添加注释
        rootElement.addElement(new TextElement(""));
        rootElement.addElement(new TextElement("<!-- 以上代码由MbgCode自动生成" + sdf.format(System.currentTimeMillis()) + " -->\n\n\n"));
        rootElement.addElement(new TextElement("<!-- 以下为您的代码 -->"));
        rootElement.addElement(new TextElement(""));

        return true;

    }

    private void addBaseResultMapColumnListXml(XmlElement parentElement, IntrospectedTable introspectedTable){
        //---------------

        XmlElement resultMap = new XmlElement("resultMap");
        resultMap.addAttribute(new Attribute("id", "BaseResultMap"));
        resultMap.addAttribute(new Attribute("type", introspectedTable.getBaseRecordType()));

        List<IntrospectedColumn> keyColumns = introspectedTable.getPrimaryKeyColumns();
        List<IntrospectedColumn> nonPrimaryKeyColumns = introspectedTable.getNonPrimaryKeyColumns();
        for(IntrospectedColumn column : keyColumns) {
            String actualColumnName = column.getActualColumnName();
            String javaProperty = column.getJavaProperty();
            String jdbcTypeName = column.getJdbcTypeName();
            XmlElement id = new XmlElement("id");
            id.addAttribute(new Attribute("column", actualColumnName));
            id.addAttribute(new Attribute("jdbcType", jdbcTypeName));
            id.addAttribute(new Attribute("property", javaProperty));
            resultMap.addElement(id);
        }
        for(IntrospectedColumn column : nonPrimaryKeyColumns) {
            String actualColumnName = column.getActualColumnName();
            String javaProperty = column.getJavaProperty();
            String jdbcTypeName = column.getJdbcTypeName();
            XmlElement result = new XmlElement("result");
            result.addAttribute(new Attribute("column", actualColumnName));
            result.addAttribute(new Attribute("jdbcType", jdbcTypeName));
            result.addAttribute(new Attribute("property", javaProperty));
            resultMap.addElement(result);
        }

        // --------------
        XmlElement sql = new XmlElement("sql");
        sql.addAttribute(new Attribute("id", "Base_Column_List"));

        List<IntrospectedColumn> columnList = introspectedTable.getAllColumns();

        XmlElement trimColumn = new XmlElement("trim");
        trimColumn.addAttribute(new Attribute("suffixOverrides", ","));

        for(IntrospectedColumn column : columnList) {
            String actualColumnName = column.getActualColumnName();
            TextElement text = new TextElement(actualColumnName + ",");
            trimColumn.addElement(text);
        }

        sql.addElement(trimColumn);

        // --------------

        parentElement.addElement(new TextElement("<!-- 结果集映射 -->"));
        parentElement.addElement(resultMap);
        parentElement.addElement(new TextElement(""));

        parentElement.addElement(new TextElement("<!-- 属性列表 -->"));
        parentElement.addElement(sql);
        parentElement.addElement(new TextElement(""));

    }

    private void addSqlLabelXml(XmlElement parentElement, IntrospectedTable introspectedTable){
        // --------------
        List<IntrospectedColumn> columnList = introspectedTable.getAllColumns();

        XmlElement insertPropertyList = new XmlElement("sql");
        insertPropertyList.addAttribute(new Attribute("id", "Insert_Property_List"));
        XmlElement insertPropertyTrimColumn = new XmlElement("trim");
        insertPropertyTrimColumn.addAttribute(new Attribute("suffixOverrides", ","));

        XmlElement selectSelectivePropertyList = new XmlElement("sql");
        selectSelectivePropertyList.addAttribute(new Attribute("id", "Select_Selective_Property_List"));
        XmlElement selectSelectivePropertyTrimColumn = new XmlElement("trim");
        selectSelectivePropertyTrimColumn.addAttribute(new Attribute("suffixOverrides", ","));

        for(IntrospectedColumn column : columnList) {
            String actualColumnName = column.getActualColumnName();
            String javaProperty = column.getJavaProperty();
            String jdbcTypeName = column.getJdbcTypeName();
            insertPropertyTrimColumn.addElement(new TextElement("#{data." + javaProperty + ",jdbcType=" + jdbcTypeName + "},"));

            XmlElement whereIf = new XmlElement("if"); //$NON-NLS-1$
            whereIf.addAttribute(new Attribute("test",  javaProperty + " != null"));
            whereIf.addElement(new TextElement("and " + actualColumnName + " = #{" + javaProperty + ",jdbcType=" + jdbcTypeName + "}"));
            selectSelectivePropertyTrimColumn.addElement(whereIf);
        }

        insertPropertyList.addElement(insertPropertyTrimColumn);
        selectSelectivePropertyList.addElement(selectSelectivePropertyTrimColumn);

        // --------------

        XmlElement updatePropertyList = new XmlElement("sql");
        updatePropertyList.addAttribute(new Attribute("id", "Update_Property_List"));
        XmlElement updatePropertyTrimColumn = new XmlElement("trim");
        updatePropertyTrimColumn.addAttribute(new Attribute("suffixOverrides", ","));

        XmlElement updatePropertySelectiveList = new XmlElement("sql");
        updatePropertySelectiveList.addAttribute(new Attribute("id", "Update_Selective_Property_List"));
        XmlElement updatePropertySelectiveTrimColumn = new XmlElement("trim");
        updatePropertySelectiveTrimColumn.addAttribute(new Attribute("suffixOverrides", ","));

        List<IntrospectedColumn> nonPrimaryKeyColumnList = introspectedTable.getNonPrimaryKeyColumns();
        for(IntrospectedColumn column : nonPrimaryKeyColumnList) {
            String actualColumnName = column.getActualColumnName();
            String javaProperty = column.getJavaProperty();
            String jdbcTypeName = column.getJdbcTypeName();

            updatePropertyTrimColumn.addElement(new TextElement(actualColumnName + " = #{data." + javaProperty + ",jdbcType=" + jdbcTypeName + "},"));

            XmlElement setIf = new XmlElement("if");
            setIf.addAttribute(new Attribute("test",  "data." +javaProperty + " != null"));
            setIf.addElement(new TextElement(actualColumnName + " = " + "#{data." + javaProperty + ",jdbcType=" + jdbcTypeName + "},"));
            updatePropertySelectiveTrimColumn.addElement(setIf);
        }

        updatePropertyList.addElement(updatePropertyTrimColumn);
        updatePropertySelectiveList.addElement(updatePropertySelectiveTrimColumn);


        // --------------
        parentElement.addElement(new TextElement("<!-- 普通插入属性列表，注意与Base_Column_List属性列表一一对应 -->"));
        parentElement.addElement(insertPropertyList);
        parentElement.addElement( new TextElement(""));

        parentElement.addElement(new TextElement("<!-- 普通修改属性列表（非主键属性） -->"));
        parentElement.addElement(updatePropertyList);
        parentElement.addElement( new TextElement(""));

        parentElement.addElement(new TextElement("<!-- 动态修改属性列表（非主键属性） -->"));
        parentElement.addElement(updatePropertySelectiveList);
        parentElement.addElement( new TextElement(""));

        parentElement.addElement(new TextElement("<!-- 动态查询属性列表（所有属性） -->"));
        parentElement.addElement(selectSelectivePropertyList);
        parentElement.addElement( new TextElement(""));

    }

    private void addRelevanceSelectColumnListXml(XmlElement parentElement, IntrospectedTable introspectedTable){
        String property = introspectedTable.getTableConfigurationProperty("selectPrefix");
        if (MbgUtil.isEmpty(property)){
            return;
        }
        property = property.toLowerCase();
        String substring0 = property.substring(0, 1);
        String substring1 = property.substring(1);
        String propertyUp0 = substring0.toUpperCase() + substring1;

        //---------------

        XmlElement resultMap = new XmlElement("resultMap");
        resultMap.addAttribute(new Attribute("id", propertyUp0 + "ResultMap"));
        resultMap.addAttribute(new Attribute("type", introspectedTable.getBaseRecordType()));

        List<IntrospectedColumn> keyColumns = introspectedTable.getPrimaryKeyColumns();
        List<IntrospectedColumn> nonPrimaryKeyColumns = introspectedTable.getNonPrimaryKeyColumns();
        for(IntrospectedColumn column : keyColumns) {
            String actualColumnName = column.getActualColumnName();
            String javaProperty = column.getJavaProperty();
            String jdbcTypeName = column.getJdbcTypeName();
            XmlElement id = new XmlElement("id");
            id.addAttribute(new Attribute("column", property + "_" + actualColumnName));
            id.addAttribute(new Attribute("jdbcType", jdbcTypeName));
            id.addAttribute(new Attribute("property", javaProperty));
            resultMap.addElement(id);
        }
        for(IntrospectedColumn column : nonPrimaryKeyColumns) {
            String actualColumnName = column.getActualColumnName();
            String javaProperty = column.getJavaProperty();
            String jdbcTypeName = column.getJdbcTypeName();
            XmlElement result = new XmlElement("result");
            result.addAttribute(new Attribute("column", property + "_" + actualColumnName));
            result.addAttribute(new Attribute("jdbcType", jdbcTypeName));
            result.addAttribute(new Attribute("property", javaProperty));
            resultMap.addElement(result);
        }

        // --------------
        XmlElement sql = new XmlElement("sql");
        sql.addAttribute(new Attribute("id", propertyUp0 + "_" + propertyUp0 + "_Column_List"));

        List<IntrospectedColumn> columnList = introspectedTable.getAllColumns();

        XmlElement trimColumn = new XmlElement("trim");
        trimColumn.addAttribute(new Attribute("suffixOverrides", ","));

        for(IntrospectedColumn column : columnList) {
            String actualColumnName = column.getActualColumnName();
            TextElement text = new TextElement(property + "." + actualColumnName + " as " + property + "_" + actualColumnName + ",");
            trimColumn.addElement(text);
        }

        sql.addElement(trimColumn);

        // --------------

        XmlElement sql1 = new XmlElement("sql");
        sql1.addAttribute(new Attribute("id", propertyUp0 + "_Base_Column_List"));

        XmlElement trimColumn1 = new XmlElement("trim");
        trimColumn1.addAttribute(new Attribute("suffixOverrides", ","));

        for(IntrospectedColumn column : columnList) {
            String actualColumnName = column.getActualColumnName();
            TextElement text = new TextElement(property + "." + actualColumnName + " as " + actualColumnName + ",");
            trimColumn1.addElement(text);
        }

        sql1.addElement(trimColumn1);

        // --------------

        parentElement.addElement( new TextElement("<!-- 表联查用的结果集映射 -->"));
        parentElement.addElement( resultMap);
        parentElement.addElement(new TextElement(""));

        parentElement.addElement(new TextElement("<!-- 表联查用的SQL属性列表，as关键字左右两边都有配置文件中配置的前缀，与【表联查用的结果集映射】配合使用 -->"));
        parentElement.addElement(sql);
        parentElement.addElement( new TextElement(""));

        parentElement.addElement( new TextElement("<!-- 表联查用的SQL属性列表，as关键字左边有配置文件中配置的前缀，与【BaseResultMap】配合使用 -->"));
        parentElement.addElement(sql1);
        parentElement.addElement( new TextElement(""));
    }

    /**
     * MBG扩展mapper方法
     */
    @Override
    public boolean clientGenerated(Interface interfaze, TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {

        interfaze.getMethods().clear();

        // 添加mapper注解
        Set<FullyQualifiedJavaType> importedTypes = new TreeSet<>();
        importedTypes.add(new FullyQualifiedJavaType("org.apache.ibatis.annotations.Mapper"));
        importedTypes.add(new FullyQualifiedJavaType("org.apache.ibatis.annotations.Param"));
        importedTypes.add(new FullyQualifiedJavaType("java.util.List"));
        importedTypes.add(new FullyQualifiedJavaType("java.util.Collection"));
        importedTypes.add(new FullyQualifiedJavaType(introspectedTable.getBaseRecordType()));
        interfaze.addImportedTypes(importedTypes);

        interfaze.addAnnotation("@Mapper");
        interfaze.addImportedTypes(importedTypes);

        // 执行扩展方法，扩展方法由子类实现
        List<Method> methodList = new ArrayList<>();
        for (IExtensionMethod extensionMethod : extensionMethodList){
            if (extensionMethod.checkConfig(ExtensionLevel.MAPPER, mbgPropertyConfig, introspectedTable)){
                methodList.addAll(extensionMethod.buildMethod(introspectedTable));
            }
        }

        for (Method method : methodList){
            List<Parameter> parameters = method.getParameters();
            for (Parameter parameter : parameters){
                importedTypes.add(parameter.getType());
                parameter.addAnnotation("@Param(\""+ parameter.getName() +"\")");
            }
            interfaze.addMethod(method);
        }

        return true;
    }


    /**
     * MBG扩展service方法
     */
    protected GeneratedJavaFile generateServiceInterface(IntrospectedTable introspectedTable){

        String serviceTargetProject = servicePropertyConfig.getServiceTargetProject();
        String servicePackage = servicePropertyConfig.getServicePackage();

        String baseRecordType = introspectedTable.getBaseRecordType();
        String modelName = baseRecordType.substring(baseRecordType.lastIndexOf(".") + 1);

        String servicePrefix = servicePropertyConfig.getServicePrefix();
        String serviceSuffix = servicePropertyConfig.getServiceSuffix();

        String serviceClassName =servicePackage + "." + servicePrefix + modelName + serviceSuffix;

        FullyQualifiedJavaType service = new FullyQualifiedJavaType(serviceClassName);
        Interface serviceInterface = new Interface(service);
        serviceInterface.setVisibility(JavaVisibility.PUBLIC);


        Set<FullyQualifiedJavaType> importedTypes = new TreeSet<>();
        importedTypes.add(new FullyQualifiedJavaType("java.util.List"));
        importedTypes.add(new FullyQualifiedJavaType("java.util.Collection"));
        importedTypes.add(new FullyQualifiedJavaType(introspectedTable.getBaseRecordType()));
        serviceInterface.addImportedTypes(importedTypes);

        // 执行扩展方法，扩展方法由子类实现
        List<Method> methodList = new ArrayList<>();
        for (IExtensionMethod extensionMethod : extensionMethodList){
            if (extensionMethod.checkConfig(ExtensionLevel.SERVICE, mbgPropertyConfig, introspectedTable)){
                methodList.addAll(extensionMethod.buildMethod(introspectedTable));
            }
        }

        for (Method method : methodList){
            List<Parameter> parameters = method.getParameters();
            for (Parameter parameter : parameters){
                importedTypes.add(parameter.getType());
            }
            serviceInterface.addMethod(method);
        }

        return new GeneratedJavaFile(serviceInterface, serviceTargetProject, context.getJavaFormatter());
    }



    /**
     * MBG扩展service实现类
     */
    protected GeneratedJavaFile generateServiceImpl(IntrospectedTable introspectedTable){

        String servicePackage = servicePropertyConfig.getServicePackage();

        String serviceImplTargetProject = servicePropertyConfig.getServiceImplTargetProject();
        String serviceImplPackage = servicePropertyConfig.getServiceImplPackage();

        String baseRecordType = introspectedTable.getBaseRecordType();
        String modelName = baseRecordType.substring(baseRecordType.lastIndexOf(".") + 1);

        String servicePrefix = servicePropertyConfig.getServicePrefix();
        String serviceSuffix = servicePropertyConfig.getServiceSuffix();
        String serviceImplPrefix = servicePropertyConfig.getServiceImplPrefix();
        String serviceImplSuffix = servicePropertyConfig.getServiceImplSuffix();

        String serviceClassName =servicePackage + "." + servicePrefix + modelName + serviceSuffix;
        String serviceImplClassName = serviceImplPackage + "." + serviceImplPrefix + modelName + serviceImplSuffix;

        String mapperFieldType = introspectedTable.getMyBatis3JavaMapperType();

        FullyQualifiedJavaType mapperJavaType = new FullyQualifiedJavaType(mapperFieldType);
        FullyQualifiedJavaType serviceJavaType = new FullyQualifiedJavaType(serviceClassName);
        FullyQualifiedJavaType serviceImplJavaType = new FullyQualifiedJavaType(serviceImplClassName);

        TopLevelClass serviceImplInterface = new TopLevelClass(serviceImplJavaType);

        Set<FullyQualifiedJavaType> importedTypes = new TreeSet<>();
        importedTypes.add(new FullyQualifiedJavaType("java.util.List"));
        importedTypes.add(new FullyQualifiedJavaType("java.util.Collection"));
        importedTypes.add(new FullyQualifiedJavaType(introspectedTable.getBaseRecordType()));
        importedTypes.add(mapperJavaType);
        importedTypes.add(serviceJavaType);
        importedTypes.add(new FullyQualifiedJavaType("org.springframework.beans.factory.annotation.Autowired"));
        importedTypes.add(new FullyQualifiedJavaType("org.springframework.stereotype.Service"));

        serviceImplInterface.addImportedTypes(importedTypes);

        //描述类的作用域修饰符
        serviceImplInterface.setVisibility(JavaVisibility.PUBLIC);

        // 添加实现类
        serviceImplInterface.addSuperInterface(serviceJavaType);
        serviceImplInterface.addAnnotation("@Service");

        String mapperFieldName = servicePropertyConfig.getMapperFieldName();

        //描述类的成员属性
        Field daoField = new Field(mapperFieldName, mapperJavaType);

        //描述成员属性修饰符
        daoField.setVisibility(JavaVisibility.PRIVATE);
        serviceImplInterface.addField(daoField);
        daoField.addAnnotation("@Autowired");

        // 执行扩展方法，扩展方法由子类实现
        List<Method> methodList = new ArrayList<>();
        for (IExtensionMethod extensionMethod : extensionMethodList){
            if (extensionMethod.checkConfig(ExtensionLevel.SERVICE, mbgPropertyConfig, introspectedTable)){
                methodList.addAll(extensionMethod.buildMethod(introspectedTable));
            }
        }

        for (Method method : methodList){
            List<Parameter> parameters = method.getParameters();
            for (Parameter parameter : parameters){
                importedTypes.add(parameter.getType());
            }
            method.addAnnotation("@Override");
            List<String> javaDocLines = method.getJavaDocLines();
            if (MbgUtil.isNotEmpty(javaDocLines)){
                javaDocLines.clear();
            }
            serviceImplInterface.addMethod(method);
        }

        // 添加方法体
        for (Method method : serviceImplInterface.getMethods()) {
            List<Parameter> parameters = method.getParameters();
            StringBuilder paramBuilder = new StringBuilder();
            if (MbgUtil.isNotEmpty(parameters)){
                for (Parameter p : parameters){
                    paramBuilder.append(p.getName()).append(", ");
                }
                paramBuilder.deleteCharAt(paramBuilder.length() - 1);
                paramBuilder.deleteCharAt(paramBuilder.length() - 1);
            }

            String methodName = method.getName();

            if (methodName.equals("add")){
                method.addBodyLine("return "+ mapperFieldName +"." + "insert" + "(" + paramBuilder + ");");
            }else if (methodName.equals("delete")){
                method.addBodyLine("return "+ mapperFieldName +"." + "deleteByPrimaryKey" + "(" + paramBuilder + ");");
            }else if (methodName.equals("update")){
                method.addBodyLine("return "+ mapperFieldName +"." + "updateByPrimaryKeySelective" + "(" + paramBuilder + ");");
            }else if (methodName.equals("detail")){
                method.addBodyLine("return "+ mapperFieldName +"." + "selectByPrimaryKey" + "(" + paramBuilder + ");");
            }else {
                method.addBodyLine("return "+ mapperFieldName +"." + method.getName() + "(" + paramBuilder + ");");
            }

        }

        return new GeneratedJavaFile(serviceImplInterface, serviceImplTargetProject, context.getJavaFormatter());

    }



    /**
     * MBG扩展controller方法
     */
    protected GeneratedJavaFile generateController(IntrospectedTable introspectedTable){

        String controllerTargetProject = controllerPropertyConfig.getControllerTargetProject();
        String controllerPackage = controllerPropertyConfig.getControllerPackage();

        String servicePackage = servicePropertyConfig.getServicePackage();

        String baseRecordType = introspectedTable.getBaseRecordType();
        String modelName = baseRecordType.substring(baseRecordType.lastIndexOf(".") + 1);


        String servicePrefix = servicePropertyConfig.getServicePrefix();
        String serviceSuffix = servicePropertyConfig.getServiceSuffix();


        String controllerPrefix = controllerPropertyConfig.getControllerPrefix();
        String controllerSuffix = controllerPropertyConfig.getControllerSuffix();

        String responseBeanClassName = controllerPropertyConfig.getResponseBeanClassName();
        String responseBeanStaticMethodName = controllerPropertyConfig.getResponseBeanStaticMethodName();
        boolean haveResponseBean = MbgUtil.isNotEmpty(responseBeanClassName) && MbgUtil.isNotEmpty(responseBeanStaticMethodName);

        String serviceClassName =servicePackage + "." + servicePrefix + modelName + serviceSuffix;
        String controllerClassName = controllerPackage + "." + controllerPrefix + modelName + controllerSuffix;

        FullyQualifiedJavaType serviceJavaType = new FullyQualifiedJavaType(serviceClassName);
        FullyQualifiedJavaType controllerJavaType = new FullyQualifiedJavaType(controllerClassName);

        TopLevelClass controllerInterface = new TopLevelClass(controllerJavaType);

        Set<FullyQualifiedJavaType> importedTypes = new TreeSet<>();
        importedTypes.add(new FullyQualifiedJavaType("java.util.List"));
        importedTypes.add(new FullyQualifiedJavaType("java.util.Collection"));
        importedTypes.add(new FullyQualifiedJavaType(introspectedTable.getBaseRecordType()));
        importedTypes.add(serviceJavaType);
        // Mapping注解包
        importedTypes.add(new FullyQualifiedJavaType("org.springframework.web.bind.annotation.*"));
        importedTypes.add(new FullyQualifiedJavaType("org.springframework.beans.factory.annotation.Autowired"));
        importedTypes.add(new FullyQualifiedJavaType("org.springframework.transaction.annotation.Transactional"));

        if (mbgPropertyConfig.getEnableSwagger()){
            importedTypes.add(new FullyQualifiedJavaType("io.swagger.annotations.Api"));
            importedTypes.add(new FullyQualifiedJavaType("io.swagger.annotations.ApiOperation"));
        }

        FullyQualifiedJavaType responseBeanJavaType = null;
        if (haveResponseBean){
            responseBeanJavaType = new FullyQualifiedJavaType(responseBeanClassName);
            importedTypes.add(responseBeanJavaType);
        }
        controllerInterface.addImportedTypes(importedTypes);

        String mappingPrefix = introspectedTable.getTableConfigurationProperty("mappingPrefix");
        if (MbgUtil.isEmpty(mappingPrefix)){
            mappingPrefix = "/" + modelName.substring(0,1).toLowerCase() + modelName.substring(1);
        }
        if (!mappingPrefix.startsWith("/")){
            mappingPrefix = "/" + mappingPrefix;
        }

        //描述类的作用域修饰符
        controllerInterface.setVisibility(JavaVisibility.PUBLIC);
        controllerInterface.addAnnotation("@RestController");
        controllerInterface.addAnnotation("@RequestMapping(value = \""+ mappingPrefix +"\")");

        if (mbgPropertyConfig.getEnableSwagger()){
            controllerInterface.addAnnotation("@Api(tags = \""+""+"\")");
        }
        String serviceFieldName = controllerPropertyConfig.getServiceFieldName();

        //描述类的成员属性
        Field daoField = new Field(serviceFieldName, serviceJavaType);
        //描述成员属性修饰符
        daoField.setVisibility(JavaVisibility.PRIVATE);
        //描述成员属性 的注解
        daoField.addAnnotation("@Autowired");
        controllerInterface.addField(daoField);


        // 执行扩展方法，扩展方法由子类实现
        List<Method> methodList = new ArrayList<>();
        for (IExtensionMethod extensionMethod : extensionMethodList){
            if (extensionMethod.checkConfig(ExtensionLevel.CONTROLLER, mbgPropertyConfig, introspectedTable)){
                methodList.addAll(extensionMethod.buildMethod(introspectedTable));
            }
        }

        for (Method method : methodList){
            String methodName = method.getName();

            List<Parameter> parameters = method.getParameters();
            String methodRemake;
            if (methodName.startsWith("insert") || methodName.startsWith("add")){
                if (parameters.size() == 1){
                    Parameter parameter = parameters.get(0);
                    importedTypes.add(parameter.getType());
                    parameter.addAnnotation("@RequestBody");
                }

                if (methodName.equals("add")){
                    methodRemake = "新增";
                }else {
                    methodRemake = methodName;
                }

                method.addAnnotation("@PutMapping(value = \"/"+ methodName +"\")");
                method.addAnnotation("@Transactional(rollbackFor = Throwable.class)");

            }else if (methodName.startsWith("delete")){
                for (Parameter parameter : parameters){
                    importedTypes.add(parameter.getType());
                    parameter.addAnnotation("@RequestParam(value = \"" + parameter.getName() + "\")");
                }

                if (methodName.equals("delete")){
                    methodRemake = "删除";
                }else {
                    methodRemake = methodName;
                }

                method.addAnnotation("@DeleteMapping(value = \"/"+ methodName +"\")");
                method.addAnnotation("@Transactional(rollbackFor = Throwable.class)");
            }else if (methodName.startsWith("update")){
                if (parameters.size() == 1){
                    Parameter parameter = parameters.get(0);
                    importedTypes.add(parameter.getType());
                    parameter.addAnnotation("@RequestBody");
                }

                if (methodName.equals("update")){
                    methodRemake = "修改";
                }else {
                    methodRemake = methodName;
                }

                method.addAnnotation("@PostMapping(value = \"/"+ methodName +"\")");
                method.addAnnotation("@Transactional(rollbackFor = Throwable.class)");
            }else if (methodName.startsWith("get") || methodName.startsWith("select") || methodName.startsWith("detail")){
                for (Parameter parameter : parameters){
                    importedTypes.add(parameter.getType());
                    parameter.addAnnotation("@RequestParam(value = \"" + parameter.getName() + "\")");
                }

                if (methodName.equals("detail")){
                    methodRemake = "详情";
                }else {
                    methodRemake = methodName;
                }

                method.addAnnotation("@GetMapping(value = \"/"+ methodName +"\")");
            }else if (methodName.startsWith("list")){
                for (Parameter parameter : parameters){
                    importedTypes.add(parameter.getType());
                    parameter.addAnnotation("@RequestParam(value = \"" + parameter.getName() + "\")");
                }

                if (methodName.equals("listQuery")){
                    methodRemake = "列表查询";
                }else {
                    methodRemake = methodName;
                }

                method.addAnnotation("@GetMapping(value = \"/"+ methodName +"\")");
            }else {
                throw new RuntimeException(String.format("MBG-code异常：非法的方法名称【%s】", methodName));
            }

            if (mbgPropertyConfig.getEnableSwagger()){
                List<String> javaDocLines = method.getJavaDocLines();
                if (MbgUtil.isNotEmpty(javaDocLines)){
                    javaDocLines.clear();
                }
                method.addAnnotation("@ApiOperation(value = \"" + methodRemake + "\", notes = \"\")");
            }

            if (haveResponseBean){
                method.setReturnType(new FullyQualifiedJavaType(responseBeanClassName));
            }


            controllerInterface.addMethod(method);
        }

        // 添加方法体
        for (Method method : controllerInterface.getMethods()) {
            List<Parameter> parameters = method.getParameters();
            StringBuilder paramBuilder = new StringBuilder();
            if (MbgUtil.isNotEmpty(parameters)){
                for (Parameter p : parameters){
                    paramBuilder.append(p.getName()).append(", ");
                }
                paramBuilder.deleteCharAt(paramBuilder.length() - 1);
                paramBuilder.deleteCharAt(paramBuilder.length() - 1);
            }

            if (haveResponseBean){
                method.addBodyLine("return " + responseBeanJavaType.getShortName() + "." + responseBeanStaticMethodName + "(" + "service." + method.getName() + "(" + paramBuilder + ")" + ");");
            }else {
                method.addBodyLine("return "+ serviceFieldName +"." + method.getName() + "(" + paramBuilder + ");");
            }
        }

        return new GeneratedJavaFile(controllerInterface, controllerTargetProject, context.getJavaFormatter());
    }




}
